package model;

import gnu.io.SerialPortEvent;
import io.CommunicationListener;
import io.IOClient;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import api.Interpreter;
import api.error.InvalidRequestException;

/**
 * The Manipulator class is a representation of a robotic manipulator.
 * The Manipulator provides access to comm ports, interpreters, and variable declarations.
 * The Manipulator handles all activity between the user program and communication with the
 * external systems. For this reason, a manipulator can be given a variable name in which
 * it can be identified.
 * @author Mike Nelson mnelson@cs.usm.maine.edu
 *
 */
public class Manipulator implements CommunicationListener, Comparable<Manipulator> {

	private Interpreter interpreter;
	private IOClient io = null;
	private Set<ManipulatorListener> listeners = new HashSet<ManipulatorListener>();
	private String variableName;

	public Manipulator(IOClient cl, Interpreter i) {
		this(cl, i, null);
	}

	public Manipulator(IOClient cl, Interpreter i, String variableName) {
		this.io = cl;
		cl.addListener(this);
		this.variableName = variableName;
		this.interpreter = i;
		cl.connect();
	}

	/**
	 * Adds a listener to this manipulator. Anytime data is sent or received, the listeners
	 * are alerted and given the data along with a reference to the manipulator.
	 * @param listen the listener to add
	 */
	public void addListener(ManipulatorListener listen) {
		listeners.add(listen);
	}

	/**
	 * Communication was requested from a comm port. All listeners will be alerted
	 */
	public void communicationRequested(SerialPortEvent event) {
		String data = new String(getIO().receive());
		List<String> datas = getInterpreter().interpretResponse(data);
		for (ManipulatorListener listen : listeners)
			listen.dataReady(this, datas);

	}

	/**
	 * Retrieves the manipulators interpreter. Converts the raw data to usable data
	 * for the control unit.
	 * @return the interpreter assigned to this manipulator
	 */
	public Interpreter getInterpreter() {
		return interpreter;
	}

	/**
	 * The IO connection to a comm port. 
	 * @return
	 */
	public IOClient getIO() {
		return this.io;
	}

	/**
	 * The variable name of this manipulator. Utilize this to enable variable-based commands.
	 * @return
	 */
	public String getVariableName() {
		return variableName;
	}

	/**
	 * Sends a command to the serial port after being filtered through the interpreter.
	 * @param command
	 * @throws InvalidRequestException when an invalid request is generated by the interpreter
	 */
	public void sendCommand(String command) throws InvalidRequestException {
		List<String> commands = getInterpreter().interpretRequest(command);
		for (String s : commands)
			getIO().send(s.getBytes());
	}

	public void setInterpreter(Interpreter interpreter) {
		this.interpreter = interpreter;
	}

	public void setVariableName(String variableName) {
		this.variableName = variableName;
	}

	public String toString() {
		return getVariableName() + " - " + getInterpreter();
	}

	public void disconnect()
	{
		getIO().disconnect();
		getIO().removeListener(this);
	}
	@Override
	public int compareTo(Manipulator arg0) {
		return this.variableName.compareTo(arg0.variableName);
	}

}
